package com.demo.common.utils;

import cn.hutool.core.lang.Pair;
import lombok.Data;

import java.awt.geom.Point2D;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 地图相关公共工具类
 * 经纬度来源要是同一种地图,否则会有误差,判断错误
 *
 * @author molong
 * @since 2022/4/1
 */
public class CommonMapUtils {

    /**
     * 判断当前位置是否在多边形区域内
     * @param lng         当前点的经度
     * @param lat         当前点的纬度
     * @param partitionLocationList 区域顶点集合
     * @return 判断结果
     */
    public static boolean isInPolygon(String lng, String lat, List<Pair<String,String>> partitionLocationList){
        double pLng =Double.parseDouble(lng);
        double pLat =Double.parseDouble(lat);
        Point2D.Double point = new Point2D.Double(pLng, pLat);

        List<Point2D.Double> pointList= new ArrayList<>();
        for (Pair<String,String> partitionLocationPair : partitionLocationList){
            //key为经度
            double polygonPointLng=Double.parseDouble(partitionLocationPair.getKey());
            //value为纬度
            double polygonPointLat=Double.parseDouble(partitionLocationPair.getValue());
            Point2D.Double polygonPoint = new Point2D.Double(polygonPointLng,polygonPointLat);
            pointList.add(polygonPoint);
        }
        return isPtInPoly(point,pointList);
    }
    /**
     * 返回一个点是否在一个多边形区域内， 如果点位于多边形的顶点或边上，不算做点在多边形内，返回false
     * @param point     待监测的点
     * @param polygon   区域的坐标点集合
     * @return 判断结果
     */
    public static boolean checkWithJdkGeneralPath(Point2D.Double point, List<Point2D.Double> polygon) {
        java.awt.geom.GeneralPath p = new java.awt.geom.GeneralPath();
        Point2D.Double first = polygon.get(0);
        p.moveTo(first.x, first.y);
        polygon.remove(0);
        for (Point2D.Double d : polygon) {
            p.lineTo(d.x, d.y);
        }
        p.lineTo(first.x, first.y);
        p.closePath();
        return p.contains(point);
    }

    /**
     * 判断点是否在多边形内，如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
     * @param point 当前检测点
     * @param pts   多边形的顶点
     * @return      点在多边形内返回true,否则返回false
     */
    public static boolean isPtInPoly(Point2D.Double point, List<Point2D.Double> pts){
        //区域顶点个数
        int n = pts.size();
        //如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
        final boolean boundOrVertex = true;
        //x的交叉点数量
        int intersectCount = 0;
        //浮点类型计算时候与0比较时候的容差
        double precision = 2e-10;
        //相邻边界顶点
        Point2D.Double p1, p2;
        //左顶点
        p1 = pts.get(0);
        //检查所有边
        for(int i = 1; i <= n; ++i){
            if(point.equals(p1)){
                //当前检测点 是一个顶点
                return boundOrVertex;
            }

            p2 = pts.get(i % n);
            if(point.x < Math.min(p1.x, p2.x) || point.x > Math.max(p1.x, p2.x)){
                p1 = p2;
                continue;
            }

            if(point.x > Math.min(p1.x, p2.x) && point.x < Math.max(p1.x, p2.x)){
                if(point.y <= Math.max(p1.y, p2.y)){
                    if(p1.x == p2.x && point.y >= Math.min(p1.y, p2.y)){
                        return boundOrVertex;
                    }

                    if(p1.y == p2.y){
                        if(p1.y == point.y){
                            return boundOrVertex;
                        }else{
                            //在边前面
                            ++intersectCount;
                        }
                    }else{
                        double xinters = (point.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
                        if(Math.abs(point.y - xinters) < precision){
                            return boundOrVertex;
                        }

                        if(point.y < xinters){
                            ++intersectCount;
                        }
                    }
                }
            }else{
                if(point.x == p2.x && point.y <= p2.y){
                    Point2D.Double p3 = pts.get((i+1) % n);
                    if(point.x >= Math.min(p1.x, p3.x) && point.x <= Math.max(p1.x, p3.x)){
                        ++intersectCount;
                    }else{
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;
        }

        //偶数在多边形外 false,奇数在多边形内 true
        return intersectCount % 2 != 0;
    }





    /**
     * 计算两组坐标的距离
     *
     * @param latA  纬度A
     * @param longA 经度A
     * @param latB  纬度B
     * @param longB 经度B
     * @return 距离米
     */
    public static BigDecimal get2PointDistance(String latA, String longA, String latB, String longB) {
        // 用户经纬对象
        Point2D pointA = new Point2D.Double(Double.parseDouble(longA), Double.parseDouble(latA));
        // 景区经纬对象
        Point2D pointB = new Point2D.Double(Double.parseDouble(longB), Double.parseDouble(latB));
        double radLat1 = rad(pointA.getY());
        double radLat2 = rad(pointB.getY());
        double a = radLat1 - radLat2;
        double b = (pointA.getX() * Math.PI / 180.0d) - (pointB.getX() * Math.PI / 180.0d);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.pow(Math.sin(b / 2), 2)))
                * 6378.138d * 1000d;
        s = Math.round(s);
        return BigDecimal.valueOf(s);
    }

    private static double rad(double d) {
        return d * Math.PI / 180.0d;
    }
}

